﻿using AutoMapper;
using Benefactor.Models.DbModels;
using Benefactor.Models.DtoModels;
using Benefactor.Services;
using Benefactor.Services.Repositories;
using Bigway.Common.Models.DtoModels;
using Bigway.Common.Services.Repositories;
using Microsoft.AspNetCore.Mvc;

namespace Benefactor.Controllers;
using static Errors;

[ApiController]
[Route("api/benefit")]
public class BenefitController(IBenefitRepository benefitRepository,
    IBenefitsCardRepository benefitsCardRepository,
    IProductRepository productRepository,
    ISoftwareSystemRepository softwareSystemRepository,
    IMapper mapper,
    IAppConfig appConfig,
    ILogger<BenefitController> logger): ControllerBase
{
    [HttpPost]
    public async Task<AddBenefitResponseDto> AddBenefitAsync(
        [FromQuery(Name = "api_key")] string apiKey,
        [FromBody] AddBenefitRequestDto request,
        CancellationToken token = default
    )
    {
        logger.LogInformation("Adding benefit for system {SystemId} and benefits card {BenefitsCardId}", 
            request.SystemId,
            request.BenefitsCardId);
        if (apiKey != appConfig.ApiKey)
            return Unauthorized<AddBenefitResponseDto>();

        var system = await softwareSystemRepository.GetAsync(request.SystemId, token);
        if (system is null)
            return SystemNotFound<AddBenefitResponseDto>(request.SystemId);

        var benefitsCardId = request.BenefitsCardId;
        var benefitsCard = await benefitsCardRepository.GetByIdAsync(benefitsCardId, token);
        if (benefitsCard is null)
            return BenefitsCardNotFound<AddBenefitResponseDto>(benefitsCardId);
        else if (benefitsCard.IsDeleted)
            return BenefitsCardDeleted<AddBenefitResponseDto>(benefitsCardId);

        var productId = request.ProductId;
        var product = await productRepository.GetByIdAsync(productId, token);
        if (product is null)
            return ProductNotFound<AddBenefitResponseDto>(productId);
        else if (product.IsDeleted)
            return ProductDeleted<AddBenefitResponseDto>(productId);

        var benefit = await benefitRepository.AddAsync(
                mapper.Map<Benefit>(request), token
            );
        benefit.Product = product;

        return new AddBenefitResponseDto
        {
            Data = mapper.Map<BenefitInfo>(benefit)
        };
    }

    [HttpPut]
    public async Task<NoDataResponseDto> UpdateBenefitAsync(
        [FromQuery(Name = "api_key")] string apiKey,
        [FromBody] UpdateBenefitRequestDto request,
        CancellationToken token = default
    )
    {
        var benefitId = request.Id;
        logger.LogInformation("Updating benefit of id {BenefitId}",
            benefitId);
        if (apiKey != appConfig.ApiKey)
            return Unauthorized<NoDataResponseDto>();

        var benefit = await benefitRepository.GetByIdAsync(benefitId, token);
        if (benefit is null)
            return BenefitNotFound<NoDataResponseDto>(benefitId);
        else if (benefit.IsDeleted)
            return BenefitDeleted<NoDataResponseDto>(benefitId);

        var productId = request.ProductId;
        var product = await productRepository.GetByIdAsync(productId, token);
        if (product is null)
            return ProductNotFound<NoDataResponseDto>(productId);
        else if (product.IsDeleted)
            return ProductDeleted<NoDataResponseDto>(productId);

        benefit = mapper.Map(request, benefit);

        await benefitRepository.UpdateAsync(benefit, token);

        return new NoDataResponseDto();
    }

    [HttpDelete]
    public async Task<NoDataResponseDto> DeleteBenefitAsync(
        [FromQuery(Name = "api_key")] string apiKey,
        [FromBody] DeleteEntityRequestDto request,
        CancellationToken token = default
    )
    {
        var benefitId = request.Id;
        logger.LogInformation("Deleting benefit of id {BenefitId}",
            benefitId);
        if (apiKey != appConfig.ApiKey)
            return Unauthorized<NoDataResponseDto>();

        var benefit = await benefitRepository.GetByIdAsync(benefitId, token);
        if (benefit is null)
            return BenefitNotFound<NoDataResponseDto>(benefitId);
        else if (benefit.IsDeleted)
            return BenefitDeleted<NoDataResponseDto>(benefitId);

        benefit.IsDeleted = true;

        await benefitRepository.UpdateAsync(benefit, token);

        return new NoDataResponseDto();
    }

    [HttpGet]
    public async Task<GetBenefitsResponseDto> GetBenefitsAsync(
        [FromQuery(Name = "api_key")] string apiKey,
        [FromQuery]GetBenefitsRequestDto request,
        CancellationToken token = default
    )
    {
        var systemId = request.system_id;
        var benefitsCardId = request.benefits_card_id;
        var benefitId = request.benefit_id;
        var productId = request.product_id;
        logger.LogInformation("Getting benefits for system {SystemId}", systemId);
        if (apiKey != appConfig.ApiKey)
            return Unauthorized<GetBenefitsResponseDto>();

        var system = await softwareSystemRepository.GetAsync(systemId, token);
        if (system is null)
            return SystemNotFound<GetBenefitsResponseDto>(systemId);

        var benefits = await benefitRepository.GetAllAsync(
            query =>
            {
                query = query.Where(x => x.SystemId == systemId && !x.IsDeleted);
                if (!string.IsNullOrEmpty(benefitId))
                    query = query.Where(x => x.Id == benefitId);

                if (!string.IsNullOrEmpty(productId))
                    query = query.Where(x => x.ProductId == productId);

                if (!string.IsNullOrEmpty(benefitsCardId)) 
                    query = query.Where(x => x.BenefitsCardId == benefitsCardId);

                query = query.Includes(x => x.Product);

                query = query.OrderByDescending(x => x.CreateTime);
                return query;
            },
            token
        );
        
        return new GetBenefitsResponseDto
        {
            Data = benefits.Select(mapper.Map<BenefitInfo>)
        };
    }
}